package cn.jh.common.pay.utils;

import cn.jh.common.pay.security.AESUtil;
import cn.jh.common.pay.security.Digest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import cn.jh.common.pay.jdpush.PayBarState;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 *
 * @author Yichuan
 * @version 1.0
 */
public class PayBarClient {

    private static final Logger LOGGER = LoggerFactory.getLogger(PayBarClient.class);
    private static final int POST_TIME_OUT = 30*1000;

    private static final String PROP_CODE = "code";
    private static final String PROP_MESSAGE = "message";
    private static final String PROP_APP_KEY = "appKey";
    private static final String PROP_DATA = "data";
    private static final String PROP_SIGN_REMOTE = "hmac";

    /**
     * post请求, 超时时间默认为: POST_TIME_OUT
     * @param url 请求url
     * @param request 请求对象
     * @return 返回请求结果
     */
    public static PayBarResponse post(String url, PayBarRequest request) {
        return post(url, request, POST_TIME_OUT);
    }

    /**
     * post请求(可指定超时时间)
     *
     * @param url 请求url
     * @param request 请求对象
     * @param timeoutMillis 超时时间,单位:毫秒
     * @return 返回请求结果
     */
    public static PayBarResponse post(String url, PayBarRequest request, int timeoutMillis) {
        /* 参数校验 */
        checkParams(url, request, timeoutMillis);

        /* 组装参数 */
        Map<String, String> postParamMap = buildHttpRequestParamMap(request);

        /* 发送post请求 */
        String responseText = HttpClient4Utils.sendHttpRequest(url, postParamMap,
                HttpClient4Utils.DEFAULT_CHARSET, true, timeoutMillis);

        /* 解析响应串,并返回 */
        return parseResponse(responseText, request);
    }

    /**
     * 参数校验
     *
     * @param url 请求url
     * @param request 请求对象
     * @param timeoutMillis 超时时间,单位:毫秒
     */
    private static void checkParams(String url, PayBarRequest request, int timeoutMillis) {
        if (timeoutMillis <= 0) {
            throw new IllegalArgumentException("超时时间设置有误,必须大于0");
        }
        if (StringUtils.isBlank(url)) {
            throw new IllegalArgumentException("url不能为空");
        }
        if (request == null) {
            throw new IllegalArgumentException("apiRequest不能为null");
        }
        if (StringUtils.isBlank(request.getMerchantNo())) {
            throw new IllegalArgumentException("[merchantNo]不能为空");
        }
        if (request.getParams() == null || request.getParams().size() == 0) {
            throw new IllegalArgumentException("apiRequest.paramMap不能为空");
        }
        if (StringUtils.isBlank(request.getSecretKey())) {
            throw new IllegalArgumentException("商户密钥[secretKey]不能为空");
        }
        if (request.getEncryptType() == null) {
            throw new IllegalArgumentException("加密算法[encryptType]不能为null");
        }
    }

    public static Map<String, String> buildHttpRequestParamMap(PayBarRequest request) {
        /* 生成加密data */
        String data = constructData(request);

        Map<String, String> httpRequestParamMap = new HashMap<>();
        httpRequestParamMap.put(PROP_DATA, data);
        httpRequestParamMap.put(PROP_APP_KEY, request.getMerchantNo());
        return httpRequestParamMap;
    }

    /**
     * 生成签名sign
     *
     * @param request 请求对象
     * @return 返回签名字符串
     */
    private static String constructSign(PayBarRequest request) {
        StringBuffer buffer = new StringBuffer();
        for (Map.Entry<String, String> entry : request.getParams().entrySet()) {
            buffer.append(entry.getValue());
        }
        return Digest.hmacSign(buffer.toString(), request.getSecretKey());
    }

    /**
     * 生成加密data
     *
     * @param request 请求对象
     * @return 返回加密字符串
     */
    private static String constructData(PayBarRequest request) {
        /* 若支持签名,则生成签名 */
        if (request.isSupportSign()) {
            String sign = constructSign(request);
            request.addParam(PROP_SIGN_REMOTE, sign);
        }
        String paramJsonStr = JSON.toJSONString(request.getParams());
        if (EncryptType.AES.equals(request.getEncryptType())) {
            /* AESUtil 加密与解密的密钥，截取商户密钥的前16位 */
            return AESUtil.encrypt(paramJsonStr, request.getSecretKey().substring(0, 16));
        } else {
            /* _3DESUtil 加密与解密的密钥，使用商户密钥的前24位 */
            //return _3DESUtil.des3EecodeToString(request.getSecretKey().substring(0, 24), paramJsonStr);
            return null;
        }
    }

    /**
     * 解析响应串
     *
     * @param responseText 请求响应字符串
     * @param request 请求对象
     * @return 返回请求响应对象
     */
    public static PayBarResponse parseResponse(String responseText, PayBarRequest request) {
        if (StringUtils.isBlank(responseText)) {
            throw new RuntimeException("responseText is blank.");
        }
        try {
            PayBarResponse response = new PayBarResponse();
            if (responseText.contains(PROP_CODE)) {
                Map<String, String> responseMap = JSON.parseObject(responseText, new TypeReference<TreeMap<String, String>>() {});
                if("200".equals(responseMap.get(PROP_CODE))){
                    response.setResultMap(responseMap);
                    response.setState(PayBarState.SUCCESS);
                    return response;
                }
                response.setState(PayBarState.FAIL);
                response.setCode(responseMap.get(PROP_CODE));
                response.setMessage(responseMap.get(PROP_MESSAGE));
                return response;
            }

            String responseJSONStr;
            if (EncryptType.AES.equals(request.getEncryptType())) {
                /* AES解密data, 使用secretKey的前16位 */
                responseJSONStr = AESUtil.decrypt(responseText, request.getSecretKey().substring(0, 16));
            } else {
                //responseJSONStr = _3DESUtil.des3DecodeToString(apiRequest.getSecretKey().substring(0, 24), responseText);
                responseJSONStr = null;
            }
            Map<String, String> resultMap = JSON.parseObject(responseJSONStr,new TypeReference<TreeMap<String, String>>() {});
            if (resultMap == null) {
                return null;
            }
            /* 请求失败时,仅返回错误码code及错误信息msg */
            if (!"200".equals(resultMap.get(PROP_CODE))) {
                response.setState(PayBarState.FAIL);
                response.setCode(resultMap.get(PROP_CODE));
                response.setMessage(resultMap.get(PROP_MESSAGE));
                return response;
            }

            /* 验签 */
            if (request.isSupportSign()) {
                /* 获取响应签名 */
                String signRemote = resultMap.get(PROP_SIGN_REMOTE);
                if (StringUtils.isBlank(signRemote)) {
                    LOGGER.error("远程服务器端未返回 {}", PROP_SIGN_REMOTE);
                    throw new RuntimeException("远程服务器端未返回" + PROP_SIGN_REMOTE);

                }

                /* 暂且去掉响应参数中的签名, 主要是为了下面的循环遍历 */
                resultMap.remove(PROP_SIGN_REMOTE);
                StringBuffer buffer = new StringBuffer();
                for (Map.Entry<String, String> entry : resultMap.entrySet()) {
                    buffer.append(entry.getValue());
                }
                resultMap.put(PROP_SIGN_REMOTE, signRemote);

                /* 本地生成签名, 然后验签 */
                String signLocal = Digest.hmacSign(buffer.toString(), request.getSecretKey());
                if (!signRemote.equals(signLocal)) {
                    LOGGER.warn("验签未通过, 本地:[{}], 服务器返回:[{}].", signLocal, signRemote);
                    throw new RuntimeException("验签未通过, 本地:[" + signLocal + "], 服务器返回:[" + signRemote + "].");
                }
            }
            response.setState(PayBarState.SUCCESS);
            response.setResultMap(resultMap);
            return response;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
